home *** CD-ROM | disk | FTP | other *** search
- ;; -*- Mode: LISP; Package: BOXER; Base: 10.; Fonts: CPTFONT -*-
-
- ;;; (C) Copyright 1985 Massachusetts Institute of Technology
- ;;;
- ;;; Permission to use, copy, modify, distribute, and sell this software
- ;;; and its documentation for any purpose is hereby granted without fee,
- ;;; provided that the above copyright notice appear in all copies and that
- ;;; both that copyright notice and this permission notice appear in
- ;;; supporting documentation, and that the name of M.I.T. not be used in
- ;;; advertising or publicity pertaining to distribution of the software
- ;;; without specific, written prior permission. M.I.T. makes no
- ;;; representations about the suitability of this software for any
- ;;; purpose. It is provided "as is" without express or implied warranty.
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
- ;;;
- ;;; This file contains the upper-level code for parsing boxes
- ;;; into LISP code. There are two procedures available to call:
- ;;;
- ;;; PARSE-BOX-INTO-LAMBDA takes a BOX as input and returns a
- ;;; lambda expression representing the box. The arglist of the
- ;;; lambda will be the arglist of the box.
- ;;;
- ;;; PARSE-INTO-CODE takes a BOX, ROW, or list of ROWS as input,
- ;;; and returns LISP-evalable code.
- ;;;
- ;;; PARSE-LIST-INTO-CODE will take a list of elements and parse
- ;;; it into code.
- ;;;
- ;;; This file is responsible for taking those type of inputs and
- ;;; getting the lowest-level elements of their rows to give to
- ;;; the Pratt parser found in PARSE2, which does the actual work
- ;;; of parsing. General parsing and special forms are dealt
- ;;; with in that file.
- ;;;
- ;;; The interface function in that file is PARSE; it takes a
- ;;; list of symbols, numbers, strings, and boxes and returns an
- ;;; evalable form which PARSE-INTO-CODE or PARSE-BOX-INTO-LAMBDA
- ;;; will glom together and wrap in something.
- ;;;
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
- ;;; Fixes for things that are broken elsewhere in the old
- ;;;release.
-
-
- (DEFMACRO PARSER-BARF (STRING &rest args)
- `(FERROR ,STRING . ,args))
-
- (defun parser-typep (object)
- (cond ((doit-box? object) ':doit-box)
- ((data-box? object) ':data-box)
- (t (typep object))))
-
- (defun parser-number-of-args (item)
- (IF (BOX? ITEM) (LENGTH (PARSER-BOXER-ARGLIST ITEM))
- (ldb %%arg-desc-min-args (boxer-args-info item))))
-
- (defun entries-on-input-row (box)
- "Returns the entries on the input row of the box, or nil of none."
- (let ((1row-entries (ROW-ENTRIES (tell box :row-at-row-no 0))))
- (IF (memq (car 1row-entries) '(bu:INPUT bu:INPUTS bu:))
- (cdr 1row-entries)
- NIL)))
-
- (DEFUN PARSER-BOXER-ARGLIST (BOX)
- "The BOXER-ARGLIST function calls the parser, so we have to have
- our own function for getting the arglist out of a doit box.
- This function should return the toplevel arglist, without any
- destructured variables."
- (check-arg-type box doit-box "a DOIT box")
- (mapcar #'(LAMBDA (entry)
- (if (label-pair? entry)
- (label-pair-label entry)
- entry))
- (entries-on-input-row box)))
-
- ;;;Returns the special arglist for destructuring. It is a list
- ;;;which has one item for each arg in the real arglist of the
- ;;;a lambda for this box. The car of each of these items is the name
- ;;;of the lisp input, as found in the bvl of the lambda.
- ;;;PARSER-BOXER-ARGLIST returns a list of these CARs (i.e., the
- ;;;lisp arglist).
- ;;;Structure of the elements of the list: After the lisp name of
- ;;;the variable comes any number of lists, one for each row in
- ;;;the destructuring box. Each list contains one or more items,
- ;;;which (as now implemented) are the names the corresponding parts
- ;;;of the input should be bound to.
-
- (DEFUN PARSER-BOXER-ARGLIST-FOR-DESTRUCTURING (BOX)
- (check-arg-type box doit-box "a DOIT box")
- (parser-destructured-args
- (entries-on-input-row box)))
-
- (defun parser-destructured-args (entry)
- (cond ((symbolp entry) entry)
- ((label-pair? entry)
- (cons (label-pair-label entry)
- (parser-destructured-args (label-pair-element entry))))
- ((listp entry)
- (mapcar #'parser-destructured-args entry))
- ((data-box? entry)
- (remq nil
- (mapcar #'(lambda (row)
- (parser-destructured-args
- (row-entries row)))
- (box-rows entry))))
- (t (parser-barf "~S -- not recognized input object" entry))))
-
- ;;;Flattens out a list. When called on a destructuring arglist, returns
- ;;;a list of all the variables involved.
- (defun flatten-list (list)
- (cond ((null list) nil)
- ((atom (car list))
- (cons (car list)
- (flatten-list (cdr list))))
- (t (nconc (flatten-list (car list))
- (flatten-list (cdr list))))))
-
-
- ;;;Given a BOX, return a lambda expression representing the box.
- ;;;The arglist of the lambda is the arglist of the box. Any
- ;;;destructuring is done by the destructuring code in the lambda.
-
- ;;;The rest of the lambda body is constructed of all the rows of
- ;;;the box run through PARSE-ROW-INTO-CODE.
-
- ;;;PARSE-ROW-INTO-CODE is given (in addition to the row) a list
- ;;;of variables (probably not yet bound) to be considered bound
- ;;;to data objects. Note that all the destructured variables
- ;;;must be included in this list. The order doesn't matter:
- ;;;it's just so PARSE-ROW-INTO-CODE will understand them when it
- ;;;comes to them.
-
- ;;;Once we allow functions as arguments the variable must be
- ;;;declared to be a function in the arglist, so we can pass that
- ;;;information along to parse-row-into-code also.
-
- (defun parse-box-into-lambda (box)
- (check-arg-type box doit-box "a DOIT box")
- (let* ((INPUTS-FOR-LAMBDA (mapcar #'(lambda (input)
- (if (box? input) ;destructured
- (gensym) ;but without a name.
- input)) ;this doesn't work right.
- (parser-boxer-arglist box)))
- (rows (if (null inputs-for-lambda)
- (box-rows box)
- (cdr (box-rows box))))
- (DESTRUCTURED-ARGUMENTS-LIST
- (parser-boxer-arglist-for-destructuring box))
- ; (local-definitions (find-local-definitions rows))
- ; (local-procedures (car local-definitions))
- ; (local-variables (cadr local-definitions))
- (arglist-variables (flatten-list destructured-arguments-list))
- (BODY
- (delq nil (mapcar #'(LAMBDA (row)
- (PARSE-ROW-INTO-CODE
- ROW
- NIL
- NIL
- ;local-variables
- ;local-procedures
- arglist-variables))
- rows))))
- (cond ((null body) `(LAMBDA () ',INPUTS-FOR-LAMBDA NIL))
- ((some destructured-arguments-list #'listp) ;Any destructuring?
- `(LAMBDA ()
- ',inputs-for-lambda ;just for show
- (*CATCH 'STOP-EXECUTING-THIS-BOX
- (bind-destructure-arguments
- ,inputs-for-lambda
- ,(parser-boxer-arglist-for-destructuring box)
- .,body))))
- (t
- `(LAMBDA ()
- ',INPUTS-FOR-LAMBDA ;just for show
- (*CATCH 'STOP-EXECUTING-THIS-BOX
- .,body))))))
-
- ;This needs to use with-boxer-bindings rather than let*.
- (defmacro bind-destructure-arguments (lambda-list destr-list &body body)
- (let ((gensym-value-list (mapcar #'(lambda (ignore) (gensym)) lambda-list)))
- `(let (,@(mapcar #'(lambda (gensym-value-name value)
- `(,gensym-value-name (box-items-list (boxer-symeval ',value))))
- gensym-value-list
- lambda-list))
- (boxer-let* ,(binding-list destr-list gensym-value-list)
- .,body))))
-
-
- ;generates a binding list given a list of destructuring patterns
- ;and the gensymmed variables containing the lists with the values.
- (defun binding-list (description-list gensym-list)
- (apply #'append ;crock
- (mapcar #'(lambda (description gensym-containing-value)
- (binding-list-1 (cdr description)
- gensym-containing-value))
- description-list
- gensym-list)))
-
- ;path is initially a gensymmed variable name containig a list of values
- ;to fit the desription, but it has cars and cdrs prepended to it.
- (defun binding-list-1 (description path)
- (if (null description) nil
- (append
- (binding-list-2 (car description) (list 'car-not-nil path))
- (binding-list-1 (cdr description) (list 'cdr-not-nil path)))))
-
-
- (defun binding-list-2 (description path)
- (if (null description) nil
- (cons (list (car description) `(car-not-nil ,path))
- (binding-list-2 (cdr description) (list 'cdr-not-nil path)))))
-
-
- (defun car-not-nil (arg)
- (if (not (null arg)) (car arg)
- (parser-barf "Some argument to the current function is a destructured box ~
- with the wrong number of elements.")))
-
- (defun cdr-not-nil (arg)
- (if (not (null arg)) (cdr arg)
- (parser-barf "Some argument to the current function is a destructured box ~
- with the wrong number of elements.")))
-
-
- ;bind-destructure-arguments is a hairy macro that converts this:
- ;(bind-destructuring-arguments
- ; (part1 part2)
- ; ((part1 (a b) (c d))
- ; (part2 (x y z)))
- ; (boxer-funcall bu:mumble a b x y z))
-
- ;into something like this:
- ;(let ((part1-list (box-items part1))
- ; (part2-list (box-items part2)))
- ; (let ((a (car (car part1-list)))
- ; (b (cadr (car part1-list)))
- ; (c (car (cadr part1-list)))
- ; (d (cadr (cadr part1-list)))
- ; (x (car (car part2-list)))
- ; (y (cadr (car part2-list)))
- ; (z (caddr (car part2-list))))
- ; (boxer-funcall bu:mumble a b x y z)))
- ;except part1-list and part2-list are GENSYMS.
-
- (defun box-items-list (box)
- (check-arg-type box data-box "a data box")
- (mapcar #'row-entries
- (box-rows box)))
-
- ;;; This takes a ROW and returns what it parses into. The
- ;result should be object that EVAL will like. Since we
- ;aren't parsing a box, there's no lambda-list to worry about.
- ;Any definitions encountered should be done.
-
- (DEFUN parse-into-code (stuff)
- (cond ((or (listp stuff) (null stuff))
- (parse-rows-as-code stuff))
- ((row? stuff) (parse (tell stuff :ENTRIES)))
- ((box? stuff) `(BOXER-FUNCALL ,(list 'QUOTE stuff)))
- ((or (numberp stuff) (stringp stuff)) stuff)
- (T
- (parser-BARF "~s cannot be parsed" STUFF))))
-
- ;;; Takes a list of rows and returns a PROGN. Again, no variables
- ;;; that aren't bound need be considered.
- (DEFUN PARSE-ROWS-AS-CODE (ROWS)
- `(PROGN .,(MAPCAR #'parse-row-into-code rows)))
-
- (DEFUN PARSE-ROW-INTO-CODE (ROW &REST ARGS)
- (LEXPR-FUNCALL #'PARSE (TELL ROW :ENTRIES) ARGS))
-
- (deff parse-list-into-code 'parse)
-
- ;Returns two values: procedures and variables defined with in
- ;the box. Things must be defined as first thing on the line.
- ;Probably some problem with label-pairs. FOO:BARbaz.
- ;Simplifying assumption:
- ;If the object following the is a DOIT-BOX, then it's a procedure,
- ;otherwise it's a variable.
- ;Returns a list of procedures (car) and variables (cadr).
- ;Each procedure is a list of the name, the doit box, and the data type.
- ;Each variable is a list of the name and the value.
-
- (DEFMACRO PARSER-PROCEDURE-SYMBOL-DESCRIPTOR-NUMBER-OF-ARGS (THING)
- `(CADDR ,THING))
-
- (DEFMACRO PARSER-PROCEDURE-SYMBOL-DESCRIPTOR-VALUE (THING)
- `(CADR ,THING))
-
- (DEFMACRO PARSER-PROCEDURE-SYMBOL-DESCRIPTOR-NAME (THING)
- `(CAR ,THING))
-
- (DEFMACRO MAKE-PARSER-PROCEDURE-SYMBOL-DESCRIPTOR (NAME VALUE NARGS)
- `(LIST ,NAME ,VALUE ,NARGS))
-
- ;(defun find-local-definitions (box-rowlist)
- ; (loop for row in box-rowlist
- ; for entry = (car (row-entries row))
- ; when (name-pair? entry)
- ; when (doit-box? (name-pair-element entry))
- ; collect (MAKE-PARSER-PROCEDURE-SYMBOL-DESCRIPTOR
- ; (name-pair-name entry)
- ; (name-pair-element entry)
- ; (parser-number-of-args (name-pair-element entry)))
- ; into procedures
- ; else collect (list (name-pair-name entry)
- ; (name-pair-element entry))
- ; into variables
- ; finally
- ; (return (list procedures variables))))
-
-
- ;Given a box, this function goes through and executes all the "" definitions
- ;in the box, and all its sub-boxes. It's for use right after READ, etc.
- ;Note that map-over-all-inferior-boxes doesn't do the current-box...
-
- ;(defun process-box-local-definitions (box)
- ; (check-box-arg box)
- ; (let ((*currently-executing-box* nil) ;Let this happen as if it were done
- ; (*boxer-binding-alist-root* nil)) ;at toplevel inside each box so it will
- ; ;side effect the boxes.
- ; (process-one-boxes-local-definitions
- ; box)
- ; (map-over-all-inferior-boxes
- ; box
- ; 'process-one-boxes-local-definitions)))
-
- (COMPILER:MAKE-OBSOLETE process-box-local-definitions "It was used for handling 's")
-
- ;(defun process-one-boxes-local-definitions (box)
- ; (let ((*boxer-static-variables-root* box))
- ; (mapc #'(lambda (row)
- ; (if (row-contains-character? row *naming-code*)
- ; (let ((entry (car (row-entries row))))
- ; (cond ((name-pair? entry)
- ; (boxer-make (name-pair-name entry)
- ; (name-pair-element entry))
- ; (if (box? (name-pair-element entry))
- ; (tell (name-pair-element entry)
- ; :set-name
- ; (name-pair-name entry))))))))
- ; (box-rows box))))
-
- (COMPILER:MAKE-OBSOLETE process-one-boxes-local-definitions "It was used for handling 's")
-
- ;temporary -- move to emanip
- (defun row-contains-character? (row character)
- (let* ((array (tell row :chas-array))
- (length (array-active-length array)))
- (do* ((i 0 (1+ i)))
- ((= i length) nil)
- (if (eq character (cha-code (aref array i)))
- (return t)))))
-